<template>
	<div
		class='m-qrcode'
		:class="{ 'qrcode-bordered': bordered }"
		:style='`width: ${size}px; height: ${size}px; border-color: ${borderColor};`'
	>
		<img :src='qrcode.value' class='qrcode-image' alt='QRCode' />
	</div>
</template>
<script setup lang='ts'>
import { computed } from "vue";
import { useQRCode } from "@vueuse/integrations/useQRCode";
import { ElMessage } from 'element-plus';

/*
	参考链接 https://developer.aliyun.com/article/1600458

  参考文档：https://vueuse.org/integrations/useQRCode/
  https://www.npmjs.com/package/qrcode#qr-code-options
*/
interface Props {
	value?: string; // 扫描后的文本或地址
	size?: number; // 二维码大小，单位 px
	color?: string; // 二维码颜色，Value must be in hex format (十六进制颜色值)
	bgColor?: string; // 二维码背景色，Value must be in hex format (十六进制颜色值)
	bordered?: boolean; // 是否有边框
	borderColor?: string; // 边框颜色
	scale?: number; // 每个 black dots 多少像素
	/*
		纠错等级也叫纠错率，就是指二维码可以被遮挡后还能正常扫描，而这个能被遮挡的最大面积就是纠错率。
		通常情况下二维码分为 4 个纠错级别：L级 可纠正约 7% 错误、M级 可纠正约 15% 错误、Q级 可纠正约 25% 错误、H级 可纠正约30% 错误。
		并不是所有位置都可以缺损，像最明显的三个角上的方框，直接影响初始定位。中间零散的部分是内容编码，可以容忍缺损。
		当二维码的内容编码携带信息比较少的时候，也就是链接比较短的时候，设置不同的纠错等级，生成的图片不会发生变化。
	*/
	errorLevel?: "L" | "M" | "Q" | "H"; // 二维码纠错等级
}
const props = withDefaults(defineProps<Props>(), {
	value: undefined,
	size: 160,
	color: "#000",
	bgColor: "#FFF",
	bordered: true,
	borderColor: "#0505050f",
	scale: 8,
	errorLevel: "H", // 可选 L M Q H
});
// console.log(props)
const qrcode = computed(() => {
	// `qrcode` will be a ref of data URL
	return useQRCode(props.value || "", {
		errorCorrectionLevel: props.errorLevel,
		type: "image/png",
		quality: 1,
		margin: 3,
		scale: props.scale, // 8px per modules(black dots)
		color: {
			dark: props.color, // 像素点颜色
			light: props.bgColor, // 背景色
		},
	});
});


/**
 * 将data URL转换为Blob对象
 * @param dataUrl 二维码的data URL
 * @returns Blob对象或null（转换失败时）
 */
const dataUrlToBlob = (dataUrl: string): Blob | null => {
	try {
		// 分离data URL的元数据和内容（如 "" → 分离为类型和base64内容）
		const [meta, base64Data] = dataUrl.split(',');
		if (!meta || !base64Data) return null;

		// 提取MIME类型（如 "image/png"）
		const mimeType = meta.match(/:(.*?);/)?.[1] || 'image/png';

		// 解码base64为二进制数据
		const byteCharacters = atob(base64Data);
		const byteArrays = [];

		// 转换为Uint8Array
		for (let i = 0; i < byteCharacters.length; i++) {
			byteArrays.push(byteCharacters.charCodeAt(i));
		}
		return new Blob([new Uint8Array(byteArrays)], { type: mimeType });
	} catch (error) {
		console.error("dataUrl转Blob失败：", error);
		return null;
	}
};

/**
 * 处理文件名（移除特殊字符，避免下载失败）
 * @param fileName 原始文件名
 * @returns 安全的文件名
 */
const sanitizeFileName = (fileName: string): string => {
	// 移除浏览器不支持的特殊字符（如 / \ : * ? " < > |）
	return fileName.replace(/[\/\\:*?"<>|]/g, '_');
};

/**
 * 下载二维码图片
 * @param fileName 自定义下载文件名（默认qrcode.png）
 */
const downloadQrCode = (fileName: string = "QRcode.png") => {
	// 1. 校验二维码是否生成
	if (!qrcode.value) {
		ElMessage.error("二维码未生成，请先设置有效内容");
		return;
	}
	// 2. 校验data URL格式
	const dataUrl = qrcode.value.value as string;


	if (!dataUrl.startsWith('data:image/')) {
		ElMessage.error("二维码格式无效，无法下载");
		return;
	}

	// 3. 转换为Blob对象
	const blob = dataUrlToBlob(dataUrl);
	if (!blob) {
		ElMessage.error("文件格式解析失败，无法下载");
		return;
	}

	// 4. 处理文件名（确保安全且以.png结尾）
	let safeFileName = sanitizeFileName(fileName);
	if (!safeFileName.endsWith('.png')) {
		safeFileName += '.png';
	}

	// 5. 创建基于Blob的下载链接
	const url = URL.createObjectURL(blob);
	const link = document.createElement('a');
	link.href = url;
	link.download = safeFileName;

	// 6. 触发下载并清理资源
	document.body.appendChild(link);
	link.click();
	document.body.removeChild(link);
	URL.revokeObjectURL(url); // 释放Object URL，避免内存泄漏

	ElMessage.success(`二维码已下载：${safeFileName}`);
};

// 暴露下载方法给父组件
defineExpose({
	downloadQrCode,
});
</script>
<style lang='scss' scoped>
.m-qrcode {
	display: inline-block;
	border-radius: 8px;
	overflow: hidden;

	.qrcode-image {
		width: 100%;
		height: 100%;
	}
}

.qrcode-bordered {
	border-width: 1px;
	border-style: solid;
}
</style>
